module heard2;

import dxml.dom;
import std.file:readText;

//import std.logger:errorf,tracef,infof,stdThreadLocalLog;

import std.json;
import jsonwrap;

import std.stdio:writeln,writefln,toFile;
import std.range.primitives:empty;
import std.algorithm.searching:find;
import std.algorithm:sort;
import std.string:format;

enum JsonPath{
    root = "/device",
    cpu = root ~ "/cpu",
    periArray = root ~ "/peripherals/peripheral",
}

enum periPath{
    root = "/",
    block = "/addressBlock",
    regArray = "/registers/register",
    intArray = "/interrupt",
    baseaddress = "/baseAddress",
    description = "/description",
    groupName = "/groupName",
    name = "/name",
}

enum regPath
{    
    root = "/",
    fieldArray = "/fields/field",
    addressOffset= "/addressOffset",
    name = "/name",
    description = "/description",
    displayName = "/displayName",
    resetValue = "/resetValue",
    size = "/size",


}

enum intPath
{
    name = "/name",
    description = "/description",
    value = "/value",
}

enum fieldPath
{
    root = "/",
    access = "/access",
    bitOffset = "/bitOffset",
    bitWidth = "/bitWidth",
    description = "/description",
    name = "/name",
}

struct accessType
{
    string str;
    alias str this;
}
struct protectionStringType
{

}

struct RegDef
{
    uint resetMask;
    uint resetValue;
    uint size;
    /// 地址单元位宽
    uint addressUnitBits;
    /// 访问宽度
    uint accessWidth;

    accessType access;
    protectionStringType protection;
    string name;
    string description;


}

/// 中断向量表结构
struct interruptVector
{
    string name;
    uint irq;
    string description;
}

alias DocType = DOMEntity!string;
alias DocAttrib = DocType.Attribute;




void makeHeader(string svdFile)
{
    auto rootJson = readSvd("STM32F401cc.svd");

    // 整理derivedFrom
    auto perisJson = rootJson.read!JSONValue(JsonPath.periArray);

    rootJson.toJSON(false,JSONOptions.doNotEscapeSlashes).toFile("tya0.json");
    /*
    foreach (ref periJson; perisJson.array)
    {
        if(periJson.exists("derivedFrom"))
        {
            auto derivedFrom = periJson.read!string("derivedFrom");

            writeln("derivedFrom:",derivedFrom);
            writeln("name:",periJson.read!string("name"));

            auto jsonStr = perisJson.findName(derivedFrom).toString();
            auto tmp = parseJSON(jsonStr);

            foreach(string mkey,JSONValue mval; periJson)
            {
                if(mkey == "derivedFrom") continue;
                tmp[mkey] = mval;
                //tmp.put(mkey,mval);
            }
            periJson = tmp;
        }
    }
    */
    rootJson.toJSON(false,JSONOptions.doNotEscapeSlashes).toFile("tya1.json");
    // 
    RegDef refGlobal;
    refGlobal.resetMask = rootJson.safe!uint(JsonPath.root~"/resetMask", uint.max);
    refGlobal.resetValue = rootJson.safe!uint(JsonPath.root~"/resetValue", uint.min);
    
    refGlobal.size = rootJson.safe!uint(JsonPath.root~"/size", 0);
    refGlobal.access = cast(accessType)rootJson.safe!string(JsonPath.root~"/access", "");
    refGlobal.addressUnitBits = rootJson.safe!uint(JsonPath.root~"/addressUnitBits", 8);
    refGlobal.accessWidth = rootJson.safe!uint(JsonPath.root~"/width", 32);
    refGlobal.name = rootJson.safe!string(JsonPath.root~"/name", "");
    refGlobal.description = rootJson.safe!string(JsonPath.root~"/description", "");

    auto intVector = makeInterruptVector(rootJson);

    auto periDecl = makePeripheral(rootJson,refGlobal);
    //findName

    //aa.toFile("tya1.json");
    //writeln("Interrupt Vectors:", intVector);
    //periDecl.toFile("tya2.d");
    //writeln("Generating header file: ", refGlobal);

    string header = "";
    header ~= format("module %s;\n", refGlobal.name);
    header ~= format("/// %s\n", refGlobal.description);
    header ~= format("import mculib.chip.util;\n");
    header ~= format("final abstract class Peripherals {\n");
    header ~= format("\tpragma(LDC_no_typeinfo):\n");
    header ~= intVector;
    header ~= periDecl;
    header ~= format("}\n");



    header.toFile(refGlobal.name~".d");


    /// 读取基本参数

}


/// 建立中断向量表
string makeInterruptVector(JSONValue json)
{
    interruptVector[] intVecs;
    bool hasIrq(uint irq)
    {
        return !(intVecs.find!((a, b) => a.irq == b)(irq).empty);
    }
    

    auto perisJson = json.read!JSONValue(JsonPath.periArray);
    

    foreach(JSONValue item; perisJson.array)
    {
        if(item.exists(periPath.intArray))
        {
            auto intsJson = item.read!JSONValue(periPath.intArray);
            interruptVector vec;

            if(intsJson.type == JSONType.array)
            {
                foreach(JSONValue intItem; intsJson.array)
                {
                    auto name = intItem.read!string("/name");
                    
                    auto irq = intItem.read!uint("/value");
                    auto description = intItem.read!string("/description");
                    vec = interruptVector(name, irq, description);
                    if(!hasIrq(vec.irq))
                    {
                        intVecs ~= vec;
                    }
                }
            }
            else
            {
                auto name = item.read!string("/interrupt/name");
                auto irq = item.read!uint("/interrupt/value");
                auto description = item.read!string("/interrupt/description");
                //interruptVector vec = {name, irq, description};
                vec = interruptVector(name, irq, description);
                //auto vec = interruptVector(item.read!string(intPath.name), item.read!uint(intPath.value), item.read!string(intPath.description));
                if(!hasIrq(vec.irq))
                {
                    intVecs ~= vec;
                }
            }            
        }
    }

    intVecs.sort!(
        (a,b) => a.irq < b.irq
    );

    auto vector = "\tenum IRQnEmum {\n";
    foreach(vec; intVecs)
    {
        vector ~= format("\t\t%s = %d,\t\t///< %s\n",
            vec.name,
            vec.irq,
            vec.description
            );
    }
    vector ~= format("\t}; // Enum size:%d\n",intVecs.length);
    return vector;
}

struct Peripheral
{
    string name;
    string description;
    string groupName;
    uint baseAddress;
    AddressBlock[] addressBlocks;
    Interrupt[] interrupts;
    Register[] registers;
    string derivedFrom;
}
/// 地址块
struct AddressBlock
{
    uint offset;
    uint size;
    string usage;
}
/// 中断
struct Interrupt
{
    string name;
    string description;
    uint irq;
}

/// 寄存器
struct Register
{
    string name;
    string description;
    string displayName;
    uint addressOffset;
    uint size;
    string access;
    uint resetValue;
    BitField[] bitFields;
}

/// 位域
struct BitField
{
    string name;
    string description;
    uint bitOffset;
    uint bitWidth;
    string access;
}



/// 外设声明
string makePeripheral(JSONValue json,RegDef refGlobal)
{
    auto perisJson = json.read!JSONValue(JsonPath.periArray);
    string periDecl = "";

    auto peris = getPeripherals(json,refGlobal);

    string[string] groupStr;

    foreach(Peripheral item; peris)
    {
        auto typename = item.name;

        if(!item.derivedFrom.empty)
        {
            typename = item.derivedFrom;
        }

        if(item.addressBlocks.length > 0)
        {
            periDecl ~= "\t";
            foreach(i,block;item.addressBlocks){
                periDecl ~= format("@AddressBlock(%s, %s, UsageToken.%s) ", block.offset, block.size, block.usage);
            }
            periDecl ~= format("\n");
        }

        if(item.interrupts.length > 0)
        {
            periDecl ~= "\t";
            foreach(irqs;item.interrupts){
                periDecl ~= format("@Interrupt(\"%s\", \"%s\", 0x%x) ", 
                irqs.name, 
                irqs.description,
                irqs.irq
                );
            }
            periDecl ~= format("\n");
        }


        periDecl ~= format(
            "\tenum %s_Type* %s = MMIO!(0x%x, %s_Type);\t//%s\n", 
            typename, 
            item.name, 
            item.baseAddress, 
            typename,
            item.description
            );
    }

    foreach (item; peris)
    {
        if(!item.derivedFrom.empty)
        {
            continue;
        }

        string mstr = "";
        mstr ~= format("\t// %s\n",item.description);
        mstr ~= format("\tstruct %s_Type {\n",item.name);
        /*
        if(item.addressBlocks.length > 0){
            mstr ~= format("\t\tenum AddressBlock[]  Block = [");
            foreach(i,block;item.addressBlocks){
                mstr ~= format("AddressBlock(%s, %s, UsageToken.%s),", block.offset, block.size, block.usage);
            }
            mstr ~= format("];\n");
        }
        */
        /*
        if(item.interrupts.length > 0)
        {
            mstr ~= format("\t\tenum Interrupt[]  IRQs = [\n");
            foreach(i,irq;item.interrupts){
                mstr ~= format("\t\t\tInterrupt(\"%s\", \"%s\", 0x%x),\n", irq.name, irq.description, irq.irq);
            }
            mstr ~= format("\t\t];\n");
        }
        */
        /// 寄存器
        if(item.registers.length > 0)
        {
            // 排序
            item.registers.sort!(
                (a,b) => a.addressOffset < b.addressOffset
            );

            //最后一个内存地址
            uint lastOffset = 0;

            
            // RO_Reg!(mask_DBGMCU_IDCODE,uint) DBGMCU_IDCODE 寄存器变量声明
            foreach(i,reg;item.registers)
            {
                auto addoffset = reg.addressOffset;
                
                if(addoffset > lastOffset)
                {
                    auto space = (addoffset - lastOffset) / 4; // uint
                    /// 填充空白
                    mstr ~= format("\t\tprivate uint[0x%x] reserved%d;\t// skip %d bytes\n",space , i,(addoffset - lastOffset));
                }

                {
                    //更新最后地址
                    lastOffset = addoffset + (reg.size / 8);
                }

                mstr ~= format("\t\t@RegInfo(\"%s\",0x%x) ", reg.name, reg.addressOffset);

                /*
                if(reg.access.empty){
                    reg.access = "";//item.access;
                }
                */

                string opAccess;

                switch(reg.access)
                {
                    case "read-only":
                        opAccess = "RO";
                        break;
                    case "write-only":
                        opAccess = "WO";
                        break;
                    case "read-write":
                        opAccess = "RW";
                        break;
                    default:
                        opAccess = "NO";
                        break;
                }


                mstr ~= format("RegVolatile!(mask_%s,",
                    reg.name
                    );

                if(reg.size == 0x20) mstr ~= "uint";
                if(reg.size == 0x10) mstr ~= "ushort";
                if(reg.size == 0x08) mstr ~= "ubyte";
                mstr ~= format(",0x%x,0x%x,RegisterAccess.%s",
                    reg.addressOffset + item.baseAddress,   /// 寄存器地址
                    reg.resetValue,                     /// 寄存器默认值
                    opAccess                            /// 寄存器访问权限
                    );

                mstr ~= format(") %s;\t\t // offset:0x%d %s %s\n",reg.name ,reg.addressOffset, reg.displayName, reg.description);
            }
            // 生成寄存器枚举
            foreach (reg; item.registers)
            {
                mstr ~= format("\t\tenum mask_%s {\n", reg.name);
                foreach(i,field;reg.bitFields)
                {   
                    //writeln("field",field);
                    string access;
                    if(field.access.empty) field.access = reg.access;

                    switch(field.access)
                    {
                        case "read-only":
                            access = "RO";
                            break;
                        case "write-only":
                            access = "WO";
                            break;
                        default:
                            access = "RW";
                            break;
                    }

                    //if(i>0) mstr ~= ",";
                    mstr ~= format("\t\t\t\t%s = RegBitField!uint(%s,%s,RegisterAccess.%s),\t\t// %s\n", 
                    field.name, 
                    field.bitOffset,
                    field.bitWidth,
                    access,
                    field.description
                    );
                }


                mstr ~= format("\t\t}\n");
            }
        }


        mstr ~= format("\t}\n");

        if(item.groupName.length > 0){
            groupStr[item.groupName] ~= mstr;
        }else{
            periDecl ~= mstr;
        }
    }

    /// 生成外设组段落
    foreach(k,vgroup;groupStr)
    {
        periDecl ~= format("\t// %s Group \n",k);

        foreach (v; vgroup)
        {
            periDecl ~= v;
        }
    }

    //writeln("Peripherals:", periDecl);
    return periDecl;
}
/// 
Peripheral[] getPeripherals(JSONValue json,RegDef refGlobal)
{
    Peripheral[] peris;
    auto perisJson = json.read!JSONValue(JsonPath.periArray);
    foreach(JSONValue item; perisJson.array)
    {
        Peripheral peri;
        peri.name = item.read!string("/name");
        peri.description = item.safe!string("/description","");
        peri.groupName = item.safe!string("/groupName","");
        peri.baseAddress = item.read!uint("/baseAddress");
        peri.derivedFrom = item.safe!string("/derivedFrom","");
        if(item.exists("/addressBlock"))
        {
            auto blockJson = item.read!JSONValue("/addressBlock");
            if(blockJson.type == JSONType.array)
            {
                foreach(JSONValue blockItem; blockJson.array)
                {
                    AddressBlock block;
                    block.offset = blockItem.safe!uint("/offset", 0);
                    block.size = blockItem.safe!uint("/size", 0);
                    block.usage = blockItem.safe!string("/usage", "");
                    peri.addressBlocks ~= block;
                }

            }else{
                AddressBlock block;
                block.offset = blockJson.safe!uint("/offset", 0);
                block.size = blockJson.safe!uint("/size", 0);
                block.usage = blockJson.safe!string("/usage", "");
                peri.addressBlocks ~= block;
            }
        }
        //peri.addressBlocks = getAddressBlocks(item);
        if(item.exists("/interrupt"))
        {
            auto intJson = item.read!JSONValue("/interrupt");
            if(intJson.type == JSONType.array)
            {
                foreach(JSONValue intItem; intJson.array)
                {
                    Interrupt ints;
                    ints.name = intItem.safe!string("/name", "");
                    ints.description = intItem.safe!string("/description", "");
                    ints.irq = intItem.safe!uint("/value", 0);
                    peri.interrupts ~= ints;
                }
            }else{
                Interrupt ints;
                ints.name = intJson.safe!string("/name", "");
                ints.description = intJson.safe!string("/description", "");
                ints.irq = intJson.safe!uint("/value", 0);
                peri.interrupts ~= ints;
            }
        }
        //peri.interrupts = getInterrupts(item);


        if(item.exists("/registers/register"))
        {
            auto regJson = item.read!JSONValue("/registers/register");
            if(regJson.type == JSONType.array)
            {
                foreach(JSONValue regItem; regJson.array)
                {
                    Register reg;
                    reg.name = regItem.safe!string("/name", "");
                    reg.description = regItem.safe!string("/description", "");
                    reg.displayName = regItem.safe!string("/displayName", "");
                    reg.addressOffset = regItem.safe!uint("/addressOffset", 0);
                    reg.size = regItem.safe!uint("/size", 0);
                    reg.access = regItem.safe!string("/access", "");
                    reg.resetValue = regItem.safe!uint("/resetValue", 0);
                    if(regItem.exists("/fields/field")){
                        reg.bitFields = getBitFields(regItem.read!JSONValue("/fields/field"),refGlobal);
                    }

                    peri.registers ~= reg;
                }
            }else{
                Register reg;
                reg.name = regJson.safe!string("/name", "");
                reg.description = regJson.safe!string("/description", "");
                reg.displayName = regJson.safe!string("/displayName", "");
                reg.addressOffset = regJson.safe!uint("/addressOffset", 0);
                reg.size = regJson.safe!uint("/size", 0);
                reg.access = regJson.safe!string("/access", "");
                reg.resetValue = regJson.safe!uint("/resetValue", 0);
                if(regJson.exists("/fields/field")){
                    reg.bitFields = getBitFields(regJson.read!JSONValue("/fields/field"),refGlobal);
                }
                peri.registers ~= reg;
            }
        }
        
        //peri.registers = getRegisters(item);

        peris ~= peri;
    }
    return peris;
}

BitField[] getBitFields(JSONValue json,RegDef refGlobal)
{
    BitField[] bitFields;
    //writeln("getBitFields",json);
    auto fieldJson = json;
    
    if(fieldJson.type == JSONType.array)
    {
        foreach(JSONValue fieldItem; fieldJson.array)
        {
            BitField field;
            field.name = fieldItem.safe!string("/name", "");
            field.description = fieldItem.safe!string("/description", "");
            field.bitOffset = fieldItem.safe!uint("/bitOffset", 0);
            field.bitWidth = fieldItem.safe!uint("/bitWidth", 0);
            field.access = fieldItem.safe!string("/access", "");
            bitFields ~= field;
        }
    }else{
        BitField field;
        field.name = fieldJson.safe!string("/name", "");
        field.description = fieldJson.safe!string("/description", "");
        field.bitOffset = fieldJson.safe!uint("/bitOffset", 0);
        field.bitWidth = fieldJson.safe!uint("/bitWidth", 0);
        field.access = fieldJson.safe!string("/access", "");
        bitFields ~= field;
    }
    return bitFields;
}



/// 读取svd文件
JSONValue readSvd(string inputFile)
{
    auto jsonRoot = JSONValue.emptyObject;
    DocType doc = parseDOM!simpleXML(inputFile.readText());
    jsonRoot = readEntity(doc.children);
    //return jsonRoot.toJSON(false,JSONOptions.doNotEscapeSlashes);
    return jsonRoot;
}

/// 递归读取xml节点
JSONValue readEntity(DocType[] node,DocAttrib[] attrib = null)
in(!node.empty)
{
    if(node.length == 1)
    {
        if ( node[0].type == EntityType.text) return jsonclean(node[0].text);
    }

    JSONValue json = JSONValue.emptyObject;

    foreach ( val; attrib)
    {
        json[val.name] = jsonclean(val.value);
    }


    foreach (item; node)
    {
        if(item.type == EntityType.text){
            assert(false,"text node is not allowed");
        }
        
        if(item.type == EntityType.elementStart)
        {
            
            if(item.name.empty){
                json = readEntity(item.children);
            }else{
                if( item.name in json)
                {
                    if(json[item.name].type != JSONType.array)
                    {
                        auto swap = json[item.name];
                        json[item.name] = JSONValue.emptyArray;
                        json[item.name].array ~= swap;
                    }
                    json[item.name].array ~= readEntity(item.children,item.attributes);
                }
                else
                {
                    json[item.name] = readEntity(item.children,item.attributes);
                }
            }
        }
    }    
    return json;
}



JSONValue jsonclean (string val)
{
    import std.bigint:BigInt;
    import std.string;
    import std.typecons;;
    
    auto str = val;
	while(str.indexOf("\n",0,No.caseSensitive)>=0) 
    {
        str = str.replace("\n", "");
    }
	while(str.indexOf("\\n",0,No.caseSensitive)>=0) 
    {
        str = str.replace("\\n", "");
    }

	while(str.indexOf("\r",0,No.caseSensitive)>=0) 
    {
        str = str.replace("\r", "");
    }
	while(str.indexOf("\\r",0,No.caseSensitive)>=0) 
    {
        str = str.replace("\\r", "");
    }

	while(str.indexOf("\t",0,No.caseSensitive)>=0) 
    {
        str = str.replace("\t", "");
    }

	while(str.indexOf("\\t",0,No.caseSensitive)>=0) 
    {
        str = str.replace("\\t", "");
    }

    while(str.indexOf("  ",0,No.caseSensitive)>=0) 
    {
        str = str.replace("  ", " ");
    }

    str = str.stripLeft().stripRight();

	
    if(str.toLower() == "true")
    {
        return JSONValue(true);
    }

    if(str.toLower() == "false")
    {
        return JSONValue(false);
    }

    try{
        ulong ul = BigInt(str).toLong();
        return JSONValue(ul);
    }catch(Exception e){
    }
	

    return JSONValue(str);
}



/// 找到数组中指定名称的元素
JSONValue findName(JSONValue json, string name)
in(json.type == JSONType.array)
{
    foreach(JSONValue item; json.array)
    {
        if( "name" in item)
        {
            if(item.safe!string("name", "") == name) return item;
        }
    }
    return JSONValue.init;
}

